﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Web.Caching;
using System.Web;

namespace Enring.Configration
{
    /// <summary>
    /// Read the basic infomation of the applicaiton.
    /// </summary>
    public class ConfigrationManager
    {
        private const string areaDirecotryName = "Areas";
        private const string applicationConfigName = "Application.xml";

        private Cache configContainer;
        private Cache applicaitonDoms;

        private Dictionary<string, string> configFilePaths;

        protected string rootPath;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path">Root path of web application</param>
        public ConfigrationManager(string path)
        {
            Init(path);
        }

        /// <summary>
        /// Read the areas and default applicaitons config
        /// </summary>
        /// <param name="path"></param>
        protected void Init(string path)
        {
            configFilePaths = new Dictionary<string, string>();

            configContainer = HttpRuntime.Cache;
            applicaitonDoms = HttpRuntime.Cache;

            if (Directory.Exists(path))
            {
                rootPath = path;
                foreach (string area in Directory.EnumerateDirectories(Path.Combine(path,areaDirecotryName)))
                {
                    XmlDocument dom = GetDomFromArea(area);
                    if (dom != null)
                    {                       
                        //Add appliaiton dom to the list
                        applicaitonDoms.Insert(Path.GetFileName(area), dom,new CacheDependency(area));
                        
                        //Read applicaiotn from applicaiton configration
                        InitApplication(area,dom.SelectSingleNode("Application"));
                    }
                }
            }
        }

        /// <summary>
        /// Get Applicaiton.xml from area directory.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public XmlDocument GetDomFromArea(string path)
        {
            XmlDocument dom = null;
            if (Directory.Exists(path))
            {
                string appPath = Path.Combine(path, applicationConfigName);
                if (File.Exists(appPath))
                {
                    dom = new XmlDocument();
                    dom.Load(appPath);
                }
            }
            
            return dom;
        }

        /// <summary>
        /// Initialize application
        /// </summary>
        /// <param name="dom"></param>
        public void InitApplication(string appPath,XmlNode dom)
        {
            string id = XmlHelper.GetAttributeValue(dom, "id").ToLower();
            //Initialize dataModel
            InitXmlDomCollection(configContainer, appPath, dom,".//DataModels/DataModel", id);

            //Initialize user interface
            InitXmlDomCollection(configContainer, appPath, dom, ".//UserInterfaces/UserInterface", id);
        }

        private void InitXmlDomCollection(Cache container,string appPath, XmlNode dom, string xpath, string id)
        {
            XmlNodeList dataModels = dom.SelectNodes(xpath);
            foreach (XmlNode dataModel in dataModels)
            {
                string xmlPath = CombinePath(appPath, dataModel);
                if (!File.Exists(xmlPath))
                    throw new CustomerException(string.Format("{1} file {0} not found.",applicaitonDoms,xpath));
                string datakey = id.Trim(".".ToCharArray()) + "." + XmlHelper.GetAttributeValue(dataModel, "id").Trim(".".ToCharArray());
                if (container[datakey] != null)
                    throw new CustomerException(string.Format("{1} {0} has duplicaiton data key", datakey,xpath));

                InitConfigFile(container, xmlPath, datakey);
            }
        }

        private static string CombinePath(string appPath, XmlNode node)
        {
            return Path.Combine(appPath, XmlHelper.GetAttributeValue(node, "path").Trim("./".ToCharArray()).Replace("/","\\"));
        }

        private XmlDocument InitConfigFile(Cache container, string xmlPath, string datakey)
        {
            XmlDocument dataDom = new XmlDocument();
            try
            {
                if (configFilePaths.ContainsKey(datakey))
                    configFilePaths.Remove(datakey);
                configFilePaths.Add(datakey, xmlPath);
                dataDom.Load(xmlPath);
                container.Insert(datakey, dataDom, new CacheDependency(xmlPath));                
            }
            catch (Exception ex)
            {
                Log.Write(ex);
            }
            return dataDom;
        }

        /// <summary>
        /// Get confgiration from file or id
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public XmlDocument GetConfigration(string key)
        {
            if(configContainer[key] != null)
                return configContainer[key] as XmlDocument;
            //Retrieve from configration
            XmlDocument dom = new XmlDocument();                    
            if (key.IndexOf("/") > -1)
            {
                string path = key;
                if (!Path.IsPathRooted(path))
                {
                    path = HttpContext.Current.Server.MapPath(key);                                        
                }
                if (File.Exists(path))
                {
                    dom.Load(path);
                    configContainer.Insert(key, dom, new CacheDependency(path));
                }
                else
                {
                    throw new CustomerException(string.Format("Configration with key {0} not found:{1}", key, path));
                }
            }
            else
            {
                if (configContainer[key] != null)
                    return configContainer[key] as XmlDocument;
                else
                {
                    //Check if key is a valid xml file, if then reaload it;
                    if (configFilePaths.ContainsKey(key))
                    {
                        //Reload xml key
                        dom = InitConfigFile(configContainer, configFilePaths[key], key);
                    }                    
                }
            }
            return dom;
        }
    }
}
